/* eslint-disable @typescript-eslint/no-explicit-any */
import { KubernetesObject } from "@kubernetes/client-node"
import { K8SResourceType, OperateFunction, PlanContextData } from "yam-types"
import { AppMonitor } from "../types.d"

let defaultServiceMonitorLabels = {
  "release": "prometheus"
}
const PROM_OPERATOR_API_GROUP = "monitoring.coreos.com"

const monitorOperator: OperateFunction<AppMonitor> = async (plan, diff, settings) => {
  defaultServiceMonitorLabels = Object.assign(defaultServiceMonitorLabels, settings.serviceMonitorLabels)
  if (!diff.hasDiff) {
    plan.log.info(`no diff found for monitoring part, skipped`)
    return
  }
  const monitors = diff.currentItems.filter(x => x.enable !== false)
  if (monitors.length === 0 && diff.deletedItems.length === 0) {
    plan.log.info(`no content found for monitoring part, skipped`)
    return
  }

  plan.action(async (ctx) => {
    if (!ctx.k8sClient.crdExists(K8SResourceType.ServiceMonitor, PROM_OPERATOR_API_GROUP)) {
      throw new Error("prometheus operator isn't installed")
    }
  }, "check-monitor-crd:" + K8SResourceType.ServiceMonitor)

  monitors.forEach(monitor => {
    const serviceMonitor = composeServiceMonitor(plan.data, monitor)
    const name = serviceMonitor.metadata?.name
    plan.action(async (ctx) => {
      ctx.mergeToYaml({
        name: "service-monitor-" + name,
        content: serviceMonitor
      })
    }, "sync-service-monitor:" + name)
  })

  diff.deletedItems.forEach(monitor => {
    const deletedMonitor = composeServiceMonitor(plan.data, monitor)
    const name = deletedMonitor.metadata?.name
    plan.action(async (ctx) => {
      await ctx.k8sClient.delete({
        name,
        kind: K8SResourceType.ServiceMonitor
      })
    }, "delete-service-monitor:" + name)
  })
}

const composeServiceMonitor = (planData: PlanContextData, monitor: AppMonitor): KubernetesObject => {
  const defaultPort = (planData.currentModelFull?.access as any)?.[0].containerPort ||
    (planData.currentModelFull?.deploy as any)?.containers[0]?.port || 80
  return {
    kind: K8SResourceType.ServiceMonitor,
    metadata: {
      name: planData.workloadName,
      labels: Object.assign({ workload: planData.workloadName }, defaultServiceMonitorLabels)
    },
    spec: {
      selector: {
        matchLabels: {
          workload: planData.workloadName
        }
      },
      endpoints: [{
        port: monitor.port || defaultPort,
        path: monitor.path || '/metrics',
        interval: monitor.intervalSeconds || '15s',
      }]
    }
  } as KubernetesObject
}

export default monitorOperator